package cool.houge.pig.infra.repository;

import cool.houge.pig.PigProps;
import cool.houge.pig.repository.weixin.Jscode2SessionInput;
import cool.houge.pig.repository.weixin.Jscode2SessionResult;
import cool.houge.pig.repository.weixin.RemoteWeixinRepository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.core.ResolvableType;
import org.springframework.http.MediaType;
import org.springframework.http.client.reactive.ClientHttpResponse;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.stereotype.Repository;
import org.springframework.web.reactive.function.BodyExtractor;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientRequestException;
import reactor.core.publisher.Mono;

/**
 * 腾讯微信远程接口.
 *
 * @author KK (kzou227@qq.com)
 */
@Repository
public class RemoteWeixinRepositoryImpl implements RemoteWeixinRepository {

  private static final Logger log = LogManager.getLogger();

  private final PigProps pigProps;
  private final WebClient thirdWebClient;

  /**
   * @param pigProps
   * @param thirdWebClient
   */
  public RemoteWeixinRepositoryImpl(PigProps pigProps, WebClient thirdWebClient) {
    this.pigProps = pigProps;
    this.thirdWebClient = thirdWebClient;
  }

  @Override
  public Mono<Jscode2SessionResult> jscode2Session(Jscode2SessionInput input) {
    return thirdWebClient
        .get()
        .uri(
            pigProps.getWeixin().getJscode2sessionUrl(),
            builder ->
                builder
                    .queryParam("appid", input.getAppId())
                    .queryParam("secret", input.getAppSecret())
                    .queryParam("js_code", input.getJsCode())
                    .queryParam("grant_type", input.getGrantType())
                    .build()
            //
            )
        .exchangeToMono(response -> response.body(textJsonExtractor(Jscode2SessionResult.class)))
        .onErrorResume(
            WebClientRequestException.class,
            e -> {
              log.warn("微信请求异常 uri={}", e.getUri(), e);
              return Mono.just(new Jscode2SessionResult().setErrcode(-1).setErrmsg(e.getMessage()));
            });
  }

  // TIPS:
  // 解析微信接口返回的 JSON 数据，因微信响应的 content-type 为 text/plain。
  // 默认的 WebClient 注册的 JSON 解码器无法解析 content-type 为 application/json 以外的数据。
  private <T> BodyExtractor<Mono<T>, ClientHttpResponse> textJsonExtractor(Class<T> elementClass) {
    var elementType = ResolvableType.forClass(elementClass);
    return (inputMessage, context) ->
        context.messageReaders().stream()
            .filter(reader -> reader.canRead(elementType, MediaType.APPLICATION_JSON))
            .findFirst()
            .map(reader -> (HttpMessageReader<T>) reader)
            .map(reader -> reader.readMono(elementType, inputMessage, context.hints()))
            .orElseThrow(() -> new IllegalStateException("微信：未找到JSON反序列化实现"));
  }
}
